package org.flashbrighton.as3bots.controllers
{
	import Box2D.Collision.*;
	import Box2D.Common.Math.*;
	import Box2D.Dynamics.*;
	import Box2D.Dynamics.Contacts.*;
	import flash.events.*;
	import org.flashbrighton.as3bots.events.*;
	import org.flashbrighton.as3bots.models.*;
	
	
	public class CollisionScorer extends EventDispatcher implements IContactListener
	{
		public static var MAX_DAMAGE:int = -50;
		
		private var _botModel1:BotModel;
		private var _botModel2:BotModel;
		private var _hitPos1:Number;
		private var _hitPos2:Number;
		private var _health1:Number;
		private var _health2:Number;
		private var _PI:Number;
		private var _twoPI:Number;
		private var _type:String;
		
		
		public function CollisionScorer()
		{			
			_PI = Math.PI;
			_twoPI = _PI * 2;
		}
		
		
		public function BeginContact(contact:b2Contact):void { }
		public function EndContact(contact:b2Contact):void { }
		public function PreSolve(contact:b2Contact, oldManifold:b2Manifold):void { }
		
		
		public function PostSolve(contact:b2Contact, impulse:b2ContactImpulse):void
		{
			var manifold:b2WorldManifold = new b2WorldManifold();
			contact.GetWorldManifold(manifold);
			calculate(manifold.m_points[0], impulse, contact.GetFixtureA().GetBody(), contact.GetFixtureB().GetBody());
			if (impulse.normalImpulses[0] > 0) notify();
		}
		
		
		public function calculate(point:b2Vec2, impulse:b2ContactImpulse, bodyA:b2Body, bodyB:b2Body):void
		{
			var impulseNormal:Number = impulse.normalImpulses[0] / 10;			
			
			_botModel1 = bodyA.GetUserData() as BotModel;
			_botModel2 = bodyB.GetUserData() as BotModel;
			
			if (!_botModel1 && !_botModel2) return; //ignore wall-wall collisions
			
			_health1 = 0;
			_health2 = 0;
			
			// Ignore persistant collisions			
			//if (impulseNormal < 0.8) maxHitScore = 0;
			
			if (_botModel1)
			{
				_type = CollidableType.BOT;
				
				_hitPos1 = getHitPos(_botModel1, point);
				_hitPos2 = getHitPos(_botModel2, point);
				
				_health1 = Math.round(Math.abs(hitPos1) * MAX_DAMAGE * impulseNormal);
				_health2 = Math.round(Math.abs(hitPos2) * MAX_DAMAGE * impulseNormal);
			}
			else
			{
				_type = CollidableType.WALL;
				
				_hitPos2 = getHitPos(_botModel2, point);								
				_health2 = Math.round(Math.abs(_hitPos2) * MAX_DAMAGE * impulseNormal);
			}
 		}
		
		
		public function notify():void
		{
			if (_botModel1)
			{
				dispatchEvent(new CollisionEvent(BotEvent.HIT, _botModel1, _hitPos1, _health1, _type));
				_botModel1 = null;
			}
			
			if (_botModel2)
			{
				dispatchEvent(new CollisionEvent(BotEvent.HIT, _botModel2, _hitPos2, _health2, _type));
				_botModel2 = null;
			}
		}
 		
 		
 		public function get hitPos1():Number
 		{
 			return _hitPos1;
 		}
 		
 		
 		public function get hitPos2():Number
 		{
 			return _hitPos2;
 		}
 		
 		
 		public function get health1():Number
 		{
 			return _health1;
 		}
 		
 		
 		public function get health2():Number
 		{
 			return _health2;
 		}
 		
		
		/**
		 * Finds the value based on where a bot gets hit (-1 to 1).
		 */
		private function getHitPos(bot:BotModel, point:b2Vec2):Number
		{
			var botAngle:Number = bot.body.GetAngle();			
			
			botAngle += _PI;
			botAngle %= _twoPI;
			if (botAngle < 0) botAngle += _twoPI;
			botAngle -= _PI;
			
			var hitAngle:Number = Math.atan2(point.y - bot.body.GetPosition().y, point.x - bot.body.GetPosition().x) - botAngle;
			var hitPos:Number = hitAngle / _PI;
			if (hitPos > 1) hitPos -= 2;
			if (hitPos < -1) hitPos += 2;

			return hitPos;
		}

	}
}